package org.ribizli.basicgameserver;

import java.util.HashMap;

public class Field {

  private static final int FIELD_SIZE = 25;

  private String playerX;
  private String playerO;
  private HashMap<String, Character> marks;
  private char[][] fields;
  private int[][] winner = new int[5][2];
  private boolean finished = false;
  private boolean[] rematch = {false, false};

  public Field(String playerX, String playerO) {
    if (playerX == null || playerO == null) {
      throw new NullPointerException();
    }
    // playerX starts on 0,0 automatically
    this.playerX = playerX;
    this.playerO = playerO;
    AmoebaGame.logger.debug("new Field: " + playerX + " - " + playerO);
    marks = new HashMap<String, Character>(2);
    marks.put(playerX, 'x');
    marks.put(playerO, 'o');
    start();
  }
  
  public void start() {
    fields = new char[FIELD_SIZE][FIELD_SIZE];
    for (int i = 0; i < FIELD_SIZE; i++) {
      for (int j = 0; j < FIELD_SIZE; j++) {
        fields[i][j] = ' ';
      }
    }
    rematch = new boolean[] {false, false};
    finished = false;
  }
  
  public void rematch(String token) {
    if (token.equals(playerX)) {
      rematch[0] = true;
    } else if (token.equals(playerO)) {
      rematch[1] = true;
    }
  }
  
  public boolean canRematch() {
    return rematch[0] == true && rematch[1] == true;
  }

  public boolean putMark(String player, int x, int y) throws GameException {
    if (finished) throw new GameException("Game already finished");
    if (fields[x][y] != ' ') {
      throw new GameException("Field not empty" + fields[x][y]);
    }
    if (x < 0 || x >= FIELD_SIZE || y < 0 || y >= FIELD_SIZE) {
      throw new GameException("Out of the field");
    }
    char mark = marks.get(player);
    fields[x][y] = mark;
    return checkWin(x, y, mark);
  }

  private boolean checkWin(int x, int y, char mark) {
    int c1, c2;
    // vertical
    c1 = countInDir(x, y, 0, 1, mark);
    c2 = countInDir(x, y, 0, -1, mark);
    if (c1 + c2 >= 4) {
      saveWinner(x, y, 0, 1, c1, c2);
      return true;
    }
    // horizontal
    c1 = countInDir(x, y, 1, 0, mark);
    c2 = countInDir(x, y, -1, 0, mark);
    if (c1 + c2 >= 4) {
      saveWinner(x, y, 1, 0, c1, c2);
      return true;
    }

    // right-down
    c1 = countInDir(x, y, 1, 1, mark);
    c2 = countInDir(x, y, -1, -1, mark);
    if (c1 + c2 >= 4) {
      saveWinner(x, y, 1, 1, c1, c2);
      return true;
    }

    // right-up
    c1 = countInDir(x, y, 1, -1, mark);
    c2 = countInDir(x, y, -1, 1, mark);
    if (c1 + c2 >= 4) {
      saveWinner(x, y, 1, -1, c1, c2);
      return true;
    }

    return false;
  }

  private void saveWinner(int x, int y, int dx, int dy, int c1, int c2) {
    finished = true;
    winner[0][0] = x;
    winner[0][1] = y;
    for (int i = 1; i <= c1; i++) {
      winner[i][0] = x + i * dx;
      winner[i][1] = y + i * dy;
    }
    for (int i = 1; i <= c2; i++) {
      winner[i + c1][0] = x - i * dx;
      winner[i + c1][1] = y - i * dy;
    }
  }

  public int[][] getWinner() {
    return winner;
  }

  public boolean isFinished() {
    return finished;
  }

  private int countInDir(int x, int y, int dx, int dy, char mark) {
    int cx = x + dx;
    int cy = y + dy;
    if (cx < 0 || cx >= FIELD_SIZE || cy < 0 || cy >= FIELD_SIZE) {
      return 0;
    }
    if (fields[cx][cy] == mark) {
      return 1 + countInDir(cx, cy, dx, dy, mark);
    }
    return 0;
  }

  public String getOpponent(String token) {
    return token.equals(playerX) ? playerO : playerX;
  }
}
